#include "BufferNotifier.h"

BufferNotifier *BufferNotifier::mInstance = nullptr;
std::mutex BufferNotifier::mInstanceMutex;

BufferListener::BufferListener()
{

}

BufferListener::~BufferListener()
{
    
}

Importer::Importer()
{
    mNotifier = BufferNotifier::getInstance();
}

Importer::~Importer()
{
    
}

void Importer::addListener(BufferListener *listener)
{
    mNotifier->addListener(listener);
}

int Importer::removeListener(int listener_id)
{
    mNotifier->removeListener(listener_id);
}

int Importer::releaseData(void *frame, void *usr_data)
{
    Importer* importer = (Importer*)usr_data;
    return importer->onReleaseData(frame);
}

int Importer::InputFrame(void* frame_data)
{
    return mNotifier->addFrame(frame_data, this, Importer::releaseData);
}

int Importer::releaseFrame(int frame_id)
{
    return mNotifier->releaseFrame(frame_id);
}

BufferNotifier *BufferNotifier::getInstance()
{
    if (mInstance == nullptr)
    {
        std::unique_lock<std::mutex> lock(mInstanceMutex);
        if (mInstance == nullptr)
        {
            mInstance = new BufferNotifier();
            return mInstance;
        }
    }
    return mInstance;
}

BufferNotifier::BufferNotifier()
:mListenerCount(0), mFrameIdCount(0)
{

}

BufferNotifier::~BufferNotifier()
{

}

int BufferNotifier::addFrame(void* frame_data, void *usr_data, usrCb release_cb)
{
    if (release_cb == nullptr)
        return -1;
    usrFrame uframe;
    uframe.frame_id = mFrameIdCount;
    uframe.frame_data = frame_data;
    uframe.usr_data = usr_data;
    uframe.release_cb = release_cb;
    uframe.ref_cnt = 0;
    usrFrame* uframe_ptr = nullptr;
    {
        std::unique_lock<std::mutex> lock(mListMutex);
        mFrameList.push_back(uframe);
        auto it = mFrameList.end();
        it--;
        uframe_ptr = &(*it);
    }
    mFrameIdCount++;
    onNotify((void*)uframe_ptr);

    return 0;
}

int BufferNotifier::releaseFrame(int frame_id)
{
    std::unique_lock<std::mutex> lock(mListMutex);
    for (std::list<usrFrame>::iterator it = mFrameList.begin(); it != mFrameList.end(); ++it)
    {
        if (it->frame_id == frame_id)
        {
            it->ref_cnt--;
            if (it->ref_cnt == 0)
            {
                it->release_cb(it->frame_data, it->usr_data);
                mFrameList.erase(it);
            }
            break;
        }
    }
    return 0;
}

int BufferNotifier::addListener(BufferListener *listener)
{
    BufferListenerProxy listener_proxy;
    listener_proxy.listener_id = mListenerCount;
    listener_proxy.listener = listener;
    std::unique_lock<std::mutex> lock(mListenerMutex);
    mFrameListener.push_back(listener_proxy);
    mListenerCount++;
    return listener_proxy.listener_id;
}

int BufferNotifier::removeListener(int listener_id)
{
    std::unique_lock<std::mutex> lock(mListenerMutex);
    for (std::vector<BufferListenerProxy>::iterator it = mFrameListener.begin(); it != mFrameListener.end(); ++it) 
    {
        if (it->listener_id == listener_id) 
        {
            delete it->listener;
            mFrameListener.erase(it);
            break;
        }
    }
}

int BufferNotifier::onNotify(void* frame)
{
    int ret = 0;
    usrFrame *usr_frame = (usrFrame*)frame;
    increaseBufRef(usr_frame);
    std::unique_lock<std::mutex> lock(mListenerMutex);
    for (std::vector<BufferListenerProxy>::iterator it = mFrameListener.begin(); it != mFrameListener.end(); ++it)
    {
        increaseBufRef((usrFrame*)frame);
        it->listener->dataCallback(usr_frame);
    }
    releaseFrame(usr_frame->frame_id);
    return ret;
}

void BufferNotifier::increaseBufRef(usrFrame* frame)
{
    frame->ref_cnt++;
}